using System;
using System.IO;
using System.Numerics;
using System.Text;
using OracleInternal.Json.Common;
using OracleInternal.Json.Sql;

namespace OracleInternal.Json.Binary
{
	internal sealed class OsonGeneratorState
	{
		internal static int SEEN_HASH_THRESHOLD = 64;

		internal static int INITIAL_OPS = 64;

		internal static int OUT_BUFFER_SIZE = 8192;

		internal int[][] m_keys = new int[256][];

		internal bool m_keysNeedReset = true;

		private int[] m_seenHash = new int[SEEN_HASH_THRESHOLD];

		private int m_seenHashSize;

		internal string[] m_distinctKeys = new string[16];

		internal int m_distinctKeysSize;

		internal byte[] m_keyHeap;

		internal int m_keyHeapSize;

		internal int[] m_keyHeapOffsets;

		internal int[] m_fidMap;

		internal int m_numOps;

		internal byte[] m_ops = new byte[INITIAL_OPS];

		internal int[] m_nextSiblings = new int[INITIAL_OPS];

		internal int[] m_fieldIDs = new int[INITIAL_OPS];

		internal byte[] m_depths = new byte[INITIAL_OPS];

		internal int[] m_numChildren = new int[INITIAL_OPS];

		internal int[] m_offsets;

		internal int m_treeSegmentSize;

		internal byte[] m_valueHeap = new byte[1024];

		internal int m_valueHeapSize;

		internal int m_tinyNodeCount;

		internal bool m_writeTinyNodeCount = true;

		internal int[] m_opStack = new int[2];

		internal byte m_depth;

		internal int m_previousSiblingIdx;

		internal int[] m_temporaryIntArray;

		internal long[] m_temporaryLongArray;

		internal StreamContext m_ctx = new StreamContext(null);

		internal BinaryWriter m_outWriter;

		internal byte[] m_outBuffer = new byte[OUT_BUFFER_SIZE];

		internal int m_outBufferPos;

		internal OsonGeneratorStatePool m_pool;

		internal OsonGeneratorState(OsonGeneratorStatePool pool, Stream outStream)
		{
			m_pool = pool;
			m_outWriter = new BinaryWriter(outStream);
			m_ctx.SetExceptionFactory(GetExceptionFactory());
		}

		internal void WriteNumber(byte[] bytes)
		{
			WriteNumber(bytes, OsonConstants.MASK_ORANUM_16, 52);
		}

		internal void WriteNumber(byte[] bytes, int mask, int bigOp)
		{
			AddValue(bytes);
			if (bytes.Length <= 8)
			{
				AddOp((bytes.Length - 1) | mask);
			}
			else if (bytes.Length < 256)
			{
				AddOp(bigOp);
			}
			m_numChildren[m_numOps - 1] = bytes.Length;
			m_ctx.Primitive();
		}

		internal void Push(int opIndex)
		{
			if (m_ctx.m_depth >= m_opStack.Length)
			{
				int num = m_opStack.Length;
				Array.Resize(ref m_opStack, num * 2);
			}
			m_opStack[m_depth] = opIndex;
			m_depth++;
			m_previousSiblingIdx = -1;
		}

		internal void AddOp(int op)
		{
			if (m_numOps >= m_ops.Length)
			{
				ExpandOp();
			}
			int num = m_numOps++;
			m_ops[num] = (byte)op;
			m_depths[num] = m_depth;
			if (m_previousSiblingIdx != -1)
			{
				m_nextSiblings[m_previousSiblingIdx] = num;
			}
			m_nextSiblings[num] = -1;
			m_numChildren[num] = 0;
			if (m_depth > 0)
			{
				m_numChildren[m_opStack[m_depth - 1]]++;
			}
			m_previousSiblingIdx = num;
		}

		internal void ExpandOp()
		{
			int newSize = m_ops.Length * 2;
			Array.Resize(ref m_ops, newSize);
			Array.Resize(ref m_nextSiblings, newSize);
			Array.Resize(ref m_fieldIDs, newSize);
			Array.Resize(ref m_depths, newSize);
			Array.Resize(ref m_numChildren, newSize);
		}

		internal void AddValue(byte[] bytes)
		{
			ExpandValueHeap(bytes.Length);
			AddValueNoCheck(bytes);
		}

		internal void AddValueNoCheck(byte[] bytes)
		{
			Array.Copy(bytes, 0, m_valueHeap, m_valueHeapSize, bytes.Length);
			m_valueHeapSize += bytes.Length;
		}

		internal void ExpandValueHeap(int len)
		{
			if (len + m_valueHeapSize >= m_valueHeap.Length)
			{
				int num = (len + m_valueHeapSize) * 2;
				if (num <= 0)
				{
					throw OracleJsonExceptions.IMAGE_TOO_BIG.Create(GetExceptionFactory());
				}
				Array.Resize(ref m_valueHeap, num);
			}
		}

		internal void InitializeKeyHeap()
		{
			if (m_keyHeap == null)
			{
				m_keyHeap = new byte[m_distinctKeysSize * 15];
			}
			if (m_keyHeapOffsets == null || m_keyHeapOffsets.Length < m_distinctKeysSize)
			{
				m_keyHeapOffsets = new int[m_distinctKeysSize];
			}
			m_keyHeapSize = 0;
			for (int i = 0; i < m_distinctKeysSize; i++)
			{
				m_keyHeapOffsets[i] = m_keyHeapSize;
				string text = m_distinctKeys[i];
				int num = 1 + text.Length * 4;
				if (num + m_keyHeapSize >= m_keyHeap.Length)
				{
					Array.Resize(ref m_keyHeap, (m_keyHeap.Length + num) * 2);
				}
				int num2 = WriteString(text, m_keyHeap, m_keyHeapSize + 1);
				int num3 = num2 - m_keyHeapSize - 1;
				if (num3 > 256)
				{
					throw OracleJsonExceptions.LONG_KEY.Create(GetExceptionFactory(), text);
				}
				m_keyHeap[m_keyHeapSize] = (byte)num3;
				m_keyHeapSize = num2;
			}
		}

		public IExceptionFactory GetExceptionFactory()
		{
			return OracleJsonExceptions.ORACLE_FACTORY;
		}

		internal int WriteString(string value, byte[] destination, int destinationPos)
		{
			int result = destinationPos;
			int length = value.Length;
			for (int i = 0; i < length; i++)
			{
				char c = value[i];
				if (c >= '\u007f')
				{
					return SlowWriteString(value, destination, destinationPos);
				}
				destination[result++] = (byte)c;
			}
			return result;
		}

		internal int WriteUTF8String(byte[] source, int sourceOffset, int len, byte[] destination, int destinationPos)
		{
			int result = destinationPos;
			for (int i = 0; i < len; i++)
			{
				destination[result++] = source[sourceOffset++];
			}
			return result;
		}

		internal int SlowWriteString(string value, byte[] destination, int destinationPos)
		{
			byte[] bytes = Encoding.UTF8.GetBytes(value);
			for (int i = 0; i < bytes.Length; i++)
			{
				destination[destinationPos++] = bytes[i];
			}
			return destinationPos;
		}

		internal void WriteHeader()
		{
			WriteUInt(4283062785u);
			short num = 6;
			if (m_distinctKeysSize >= 65536)
			{
				num = (short)(num | 8);
			}
			else if (m_distinctKeysSize >= 256)
			{
				num = (short)(num | 0x400);
			}
			if (m_distinctKeysSize > 0)
			{
				num = (short)(num | 0x100);
			}
			if (m_keyHeapSize >= 65536)
			{
				num = (short)(num | 0x800);
			}
			if (m_treeSegmentSize > 65536)
			{
				num = (short)(num | 0x1000);
			}
			if (m_numOps == 1 && !IsObject(m_ops[0]) && !IsArray(m_ops[0]))
			{
				num = (short)(num | 0x10);
				WriteShort(num);
				WriteTreeSegmentSize();
				return;
			}
			if (m_writeTinyNodeCount)
			{
				num = (short)(num | 0x2000);
			}
			WriteShort(num);
			if (m_distinctKeysSize >= 65536)
			{
				WriteInt(m_distinctKeysSize);
			}
			else if (m_distinctKeysSize >= 256)
			{
				WriteShort(m_distinctKeysSize);
			}
			else
			{
				WriteByte(m_distinctKeysSize);
			}
			if (m_keyHeapSize >= 65536)
			{
				WriteInt(m_keyHeapSize);
			}
			else
			{
				WriteShort(m_keyHeapSize);
			}
			WriteTreeSegmentSize();
			if (m_writeTinyNodeCount)
			{
				WriteShort(m_tinyNodeCount);
			}
			else
			{
				WriteShort(0);
			}
		}

		internal void WriteTreeSegmentSize()
		{
			if (m_treeSegmentSize > 65536)
			{
				WriteInt(m_treeSegmentSize);
			}
			else
			{
				WriteShort(m_treeSegmentSize);
			}
		}

		internal void WriteNameDictionary()
		{
			if (m_fidMap == null || m_fidMap.Length < m_distinctKeysSize)
			{
				m_fidMap = new int[m_distinctKeysSize];
			}
			InitTemporaryIntArray(m_distinctKeysSize);
			if (m_seenHashSize < SEEN_HASH_THRESHOLD)
			{
				Array.Sort(m_seenHash, 0, m_seenHashSize);
				int offIdx = 0;
				for (int i = 0; i < m_seenHashSize; i++)
				{
					int hash = m_seenHash[i];
					offIdx = ProcessBucket(offIdx, hash);
				}
			}
			else
			{
				int offIdx2 = 0;
				for (int j = 0; j < m_keys.Length; j++)
				{
					if (m_keys[j] != null)
					{
						offIdx2 = ProcessBucket(offIdx2, j);
					}
				}
			}
			m_keysNeedReset = false;
			if (m_keyHeapSize >= 65536)
			{
				WriteUb4Array(m_temporaryIntArray, m_distinctKeysSize);
			}
			else
			{
				writeUb2Array(m_temporaryIntArray, m_distinctKeysSize);
			}
			Write(m_keyHeap, 0, m_keyHeapSize);
		}

		internal int ProcessBucket(int offIdx, int hash)
		{
			int[] array = m_keys[hash];
			SortBucket(array);
			for (int i = 0; i < array.Length; i++)
			{
				int num = array[i] - 1;
				if (num == -1)
				{
					break;
				}
				WriteByte(hash);
				array[i] = 0;
				m_fidMap[num] = offIdx;
				m_temporaryIntArray[offIdx++] = m_keyHeapOffsets[num];
			}
			return offIdx;
		}

		internal void SortBucket(int[] bucket)
		{
			for (int i = 0; i < bucket.Length && bucket[i] != 0; i++)
			{
				for (int j = i + 1; j < bucket.Length && bucket[j] != 0; j++)
				{
					int num = m_keyHeapOffsets[bucket[i] - 1];
					int num2 = m_keyHeapOffsets[bucket[j] - 1];
					int num3 = m_keyHeap[num] & 0xFF;
					int num4 = m_keyHeap[num2] & 0xFF;
					if (num4 < num3 || (num4 == num3 && Memcmp(num2 + 1, num + 1, num3) < 0))
					{
						int num5 = bucket[i];
						bucket[i] = bucket[j];
						bucket[j] = num5;
					}
				}
			}
		}

		internal int Memcmp(int i, int j, int length)
		{
			for (int k = 0; k < length; k++)
			{
				int num = (m_keyHeap[i + k] & 0xFF) - (m_keyHeap[j + k] & 0xFF);
				if (num != 0)
				{
					return num;
				}
			}
			return 0;
		}

		internal void writeTreeNodeSegment()
		{
			int num = 0;
			for (int i = 0; i < m_numOps; i++)
			{
				int num2 = m_ops[i];
				if (IsArray(num2))
				{
					int num3 = m_numChildren[i];
					WriteByte(FlagObjectOrArray(num2, num3));
					if (num3 < 256)
					{
						WriteByte(num3);
					}
					else if (num3 < 65536)
					{
						WriteShort(num3);
					}
					else
					{
						WriteInt(num3);
					}
					InitTemporaryIntArray(num3);
					int num4 = i + 1;
					for (int j = 0; j < num3; j++)
					{
						m_temporaryIntArray[j] = m_offsets[num4];
						num4 = m_nextSiblings[num4];
					}
					WriteChildOffests(num3, m_temporaryIntArray);
					continue;
				}
				if (IsObject(num2))
				{
					int num5 = m_numChildren[i];
					WriteByte(FlagObject(num2, num5));
					InitTemporaryLongArray(num5);
					if (SharesFields(num2))
					{
						int num6 = FirstChild(i);
						int num7 = m_fieldIDs[num6];
						int value = m_offsets[num7];
						m_fieldIDs[num6] = m_fieldIDs[FirstChild(num7)];
						if (m_treeSegmentSize < 65536)
						{
							WriteShort(value);
						}
						else
						{
							WriteInt(value);
						}
						PackOffsets(i, num5, m_temporaryLongArray);
						if (num5 > 10)
						{
							Array.Sort(m_temporaryLongArray, 0, num5);
						}
						WriteChildOffests(num5, m_temporaryLongArray);
						continue;
					}
					PackOffsets(i, num5, m_temporaryLongArray);
					if (num5 > 10)
					{
						Array.Sort(m_temporaryLongArray, 0, num5);
					}
					if (num5 < 256)
					{
						WriteByte(num5);
					}
					else if (num5 < 65536)
					{
						WriteShort(num5);
					}
					else
					{
						WriteInt(num5);
					}
					if (m_distinctKeysSize >= 65536)
					{
						for (int k = 0; k < num5; k++)
						{
							WriteInt((int)(m_temporaryLongArray[k] >> 32));
						}
					}
					else if (m_distinctKeysSize >= 256)
					{
						for (int l = 0; l < num5; l++)
						{
							WriteShort((int)(m_temporaryLongArray[l] >> 32));
						}
					}
					else
					{
						for (int m = 0; m < num5; m++)
						{
							WriteByte((int)(m_temporaryLongArray[m] >> 32));
						}
					}
					WriteChildOffests(num5, m_temporaryLongArray);
					continue;
				}
				if (num2 <= 31)
				{
					WriteOpAndData(num2, m_valueHeap, num, num2);
					num += num2;
					continue;
				}
				if (OsonConstants.IsSB4(num2) || OsonConstants.IsSB8(num2) || OsonConstants.IsOraNum16(num2) || OsonConstants.IsDec_16(num2))
				{
					WriteByte(num2);
					Write(m_valueHeap, num, m_numChildren[i]);
					num += m_numChildren[i];
					continue;
				}
				WriteByte(num2);
				switch (num2)
				{
				case 51:
				case 52:
				case 116:
				{
					int num14 = m_numChildren[i];
					WriteByte(num14);
					Write(m_valueHeap, num, num14);
					num += num14;
					break;
				}
				case 55:
				{
					int num13 = m_numChildren[i];
					WriteShort(num13);
					Write(m_valueHeap, num, num13);
					num += num13;
					break;
				}
				case 56:
				{
					int num12 = m_numChildren[i];
					WriteInt(num12);
					Write(m_valueHeap, num, num12);
					num += num12;
					break;
				}
				case 54:
					Write(m_valueHeap, num, 8);
					num += 8;
					break;
				case 127:
					Write(m_valueHeap, num, 4);
					num += 4;
					break;
				case 126:
				{
					int num11 = m_numChildren[i];
					WriteByte(num11);
					Write(m_valueHeap, num, num11);
					num += num11;
					break;
				}
				case 58:
				{
					int num10 = m_numChildren[i];
					WriteShort(num10);
					Write(m_valueHeap, num, num10);
					num += num10;
					break;
				}
				case 59:
				{
					int num9 = m_numChildren[i];
					WriteInt(num9);
					Write(m_valueHeap, num, num9);
					num += num9;
					break;
				}
				case 57:
					Write(m_valueHeap, num, OsonPrimitiveConversions.SIZE_TIMESTAMP);
					num += OsonPrimitiveConversions.SIZE_TIMESTAMP;
					break;
				case 125:
					Write(m_valueHeap, num, OsonPrimitiveConversions.SIZE_TIMESTAMP_NOFRAC);
					num += OsonPrimitiveConversions.SIZE_TIMESTAMP_NOFRAC;
					break;
				case 60:
					Write(m_valueHeap, num, OsonPrimitiveConversions.SIZE_DATE);
					num += OsonPrimitiveConversions.SIZE_DATE;
					break;
				case 62:
					Write(m_valueHeap, num, 11);
					num += 11;
					break;
				case 61:
					Write(m_valueHeap, num, 5);
					num += 5;
					break;
				case 53:
				{
					int num8 = m_numChildren[i];
					WriteByte(num8);
					Write(m_valueHeap, num, num8);
					num += num8;
					break;
				}
				default:
					throw new NotSupportedException(num2.ToString());
				case 48:
				case 49:
				case 50:
					break;
				}
			}
		}

		internal void PackOffsets(int index, int childCt, long[] packedArray)
		{
			int num = index + 1;
			for (int i = 0; i < childCt; i++)
			{
				int num2 = m_fieldIDs[num];
				long num3 = m_fidMap[num2] + 1;
				packedArray[i] = (num3 << 32) | m_offsets[num];
				num = m_nextSiblings[num];
			}
		}

		internal bool SharesFields(int op)
		{
			return (op & 0x18) == 24;
		}

		internal bool IsReferredTo(int op)
		{
			return (op & 2) == 2;
		}

		internal void TryFieldIdSharing(int primaryIndex)
		{
			for (int num = m_nextSiblings[primaryIndex]; num != -1; num = m_nextSiblings[num])
			{
				if (SameFieldIds(primaryIndex, num))
				{
					m_ops[num] |= 24;
					m_ops[primaryIndex] |= 2;
					int num2 = FirstChild(num);
					m_fieldIDs[num2] = primaryIndex;
				}
			}
		}

		internal int FirstChild(int index)
		{
			if (index + 1 >= m_numOps)
			{
				return -1;
			}
			int num = m_depths[index];
			if (m_depths[index + 1] == num + 1)
			{
				return index + 1;
			}
			return -1;
		}

		internal bool SameFieldIds(int p1, int p2)
		{
			if (m_numChildren[p1] != m_numChildren[p2] || m_numChildren[p1] == 0 || IsArray(m_ops[p1]) || IsArray(m_ops[p2]))
			{
				return false;
			}
			int num = FirstChild(p1);
			int num2 = FirstChild(p2);
			do
			{
				if (m_fieldIDs[num] != m_fieldIDs[num2])
				{
					return false;
				}
				num = m_nextSiblings[num];
				num2 = m_nextSiblings[num2];
				if (num == -1)
				{
					return num2 == -1;
				}
			}
			while (num2 != -1);
			return false;
		}

		internal bool IsArray(int op)
		{
			return (op & 0xC0) == 192;
		}

		internal bool IsObject(int op)
		{
			return (op & 0xC0) == 128;
		}

		internal bool IsStructure(int op)
		{
			return op < 0;
		}

		internal void WriteChildOffests(int childCt, long[] arr)
		{
			if (m_treeSegmentSize < 65536)
			{
				for (int i = 0; i < childCt; i++)
				{
					WriteShort((int)(arr[i] & 0xFFFF));
				}
			}
			else
			{
				for (int j = 0; j < childCt; j++)
				{
					WriteInt((int)(arr[j] & 0xFFFFFFFFu));
				}
			}
		}

		internal void WriteChildOffests(int childCt, int[] arr)
		{
			if (m_treeSegmentSize < 65536)
			{
				for (int i = 0; i < childCt; i++)
				{
					WriteShort(arr[i]);
				}
			}
			else
			{
				for (int j = 0; j < childCt; j++)
				{
					WriteInt((int)(arr[j] & 0xFFFFFFFFu));
				}
			}
		}

		internal void InitTemporaryLongArray(int ct)
		{
			if (m_temporaryLongArray == null || m_temporaryLongArray.Length < ct)
			{
				m_temporaryLongArray = new long[ct];
			}
		}

		internal void InitTemporaryIntArray(int ct)
		{
			if (m_temporaryIntArray == null || m_temporaryIntArray.Length < ct)
			{
				m_temporaryIntArray = new int[ct];
			}
		}

		internal void ComputeOffsets()
		{
			if (m_offsets == null || m_numOps > m_offsets.Length)
			{
				m_offsets = new int[m_numOps];
			}
			int num = 0;
			m_tinyNodeCount = 0;
			for (int i = 0; i < m_numOps; i++)
			{
				m_offsets[i] = num;
				if (IsFirstChildObjectOfArray(i))
				{
					TryFieldIdSharing(i);
				}
				int num2 = SizeOfOp(i, 2);
				CountTiny(i, num2);
				num += num2;
				if (num >= 65536)
				{
					num = -1;
					break;
				}
			}
			if (num != -1)
			{
				m_treeSegmentSize = num;
				return;
			}
			num = 0;
			m_tinyNodeCount = 0;
			for (int j = 0; j < m_numOps; j++)
			{
				m_offsets[j] = num;
				if (IsFirstChildObjectOfArray(j))
				{
					TryFieldIdSharing(j);
				}
				int num3 = SizeOfOp(j, 4);
				CountTiny(j, num3);
				num += num3;
				if (num < 0)
				{
					throw OracleJsonExceptions.IMAGE_TOO_BIG.Create(GetExceptionFactory());
				}
			}
			m_treeSegmentSize = num;
		}

		internal void CountTiny(int i, int size)
		{
			if (IsStructure(m_ops[i]) && (size < 5 || (IsObject(m_ops[i]) && IsReferredTo(m_ops[i]))))
			{
				m_tinyNodeCount++;
			}
		}

		internal bool IsFirstChildObjectOfArray(int i)
		{
			if (IsObject(m_ops[i]) && i > 0 && FirstChild(i - 1) == i)
			{
				return IsArray(m_ops[i - 1]);
			}
			return false;
		}

		internal int SizeOfOp(int index, int offsetSize)
		{
			int num = m_ops[index];
			if (IsArray(num))
			{
				int num2 = m_numChildren[index];
				int num3 = BytesForNum(num2);
				return 1 + num3 + offsetSize * num2;
			}
			if (IsObject(num))
			{
				int num4 = m_numChildren[index];
				if (SharesFields(num))
				{
					return 1 + offsetSize + num4 * offsetSize;
				}
				int num5 = BytesForNum(num4);
				int num6 = num4;
				if (m_distinctKeysSize >= 65536)
				{
					num6 *= 4;
				}
				else if (m_distinctKeysSize >= 256)
				{
					num6 *= 2;
				}
				return 1 + num5 + num6 + num4 * offsetSize;
			}
			if (num <= 31)
			{
				return 1 + num;
			}
			if (OsonConstants.IsSB4(num) || OsonConstants.IsSB8(num) || OsonConstants.IsOraNum16(num) || OsonConstants.IsDec_16(num))
			{
				return 1 + m_numChildren[index];
			}
			switch (num)
			{
			case 48:
			case 49:
			case 50:
				return 1;
			case 51:
				return 2 + m_numChildren[index];
			case 55:
				return 3 + m_numChildren[index];
			case 56:
				return 5 + m_numChildren[index];
			case 52:
			case 116:
				return 2 + m_numChildren[index];
			case 54:
				return 9;
			case 127:
				return 5;
			case 58:
				return 3 + m_numChildren[index];
			case 59:
				return 5 + m_numChildren[index];
			case 126:
				return 2 + m_numChildren[index];
			case 57:
				return OsonPrimitiveConversions.SIZE_TIMESTAMP + 1;
			case 125:
				return OsonPrimitiveConversions.SIZE_TIMESTAMP_NOFRAC + 1;
			case 124:
				return OsonPrimitiveConversions.SIZE_TIMESTAMPTZ + 1;
			case 60:
				return OsonPrimitiveConversions.SIZE_DATE + 1;
			case 62:
				return 12;
			case 61:
				return 6;
			case 53:
				return 2 + m_numChildren[index];
			default:
				throw new NotSupportedException(num.ToString());
			}
		}

		internal int BytesForNum(int i)
		{
			if (i < 256)
			{
				return 1;
			}
			if (i < 65536)
			{
				return 2;
			}
			return 4;
		}

		internal int FlagObject(int op, int numChildren)
		{
			if (numChildren <= 10)
			{
				return FlagObjectOrArray(op, numChildren) | 4;
			}
			return FlagObjectOrArray(op, numChildren);
		}

		internal int FlagObjectOrArray(int op, int numChildren)
		{
			if (numChildren >= 256)
			{
				op = ((numChildren >= 65536) ? (op | 0x10) : (op | 8));
			}
			if (m_treeSegmentSize > 65536)
			{
				op |= 0x20;
			}
			return op;
		}

		internal void writeUb2Array(int[] arr, int len)
		{
			for (int i = 0; i < len; i++)
			{
				WriteShort(arr[i]);
			}
		}

		internal void WriteUb4Array(int[] arr, int len)
		{
			for (int i = 0; i < len; i++)
			{
				WriteInt(arr[i]);
			}
		}

		internal void WriteInt(int value)
		{
			if (m_outBufferPos + 3 >= m_outBuffer.Length)
			{
				FlushBuffer();
			}
			m_outBuffer[m_outBufferPos++] = (byte)((uint)(value >> 24) & 0xFFu);
			m_outBuffer[m_outBufferPos++] = (byte)((uint)(value >> 16) & 0xFFu);
			m_outBuffer[m_outBufferPos++] = (byte)((uint)(value >> 8) & 0xFFu);
			m_outBuffer[m_outBufferPos++] = (byte)((uint)value & 0xFFu);
		}

		internal void WriteUInt(uint value)
		{
			if (m_outBufferPos + 3 >= m_outBuffer.Length)
			{
				FlushBuffer();
			}
			m_outBuffer[m_outBufferPos++] = (byte)((value >> 24) & 0xFFu);
			m_outBuffer[m_outBufferPos++] = (byte)((value >> 16) & 0xFFu);
			m_outBuffer[m_outBufferPos++] = (byte)((value >> 8) & 0xFFu);
			m_outBuffer[m_outBufferPos++] = (byte)(value & 0xFFu);
		}

		internal void WriteShort(int value)
		{
			if (m_outBufferPos + 1 >= m_outBuffer.Length)
			{
				FlushBuffer();
			}
			m_outBuffer[m_outBufferPos++] = (byte)((uint)(value >> 8) & 0xFFu);
			m_outBuffer[m_outBufferPos++] = (byte)((uint)value & 0xFFu);
		}

		internal void WriteByte(int b)
		{
			if (m_outBufferPos >= m_outBuffer.Length)
			{
				FlushBuffer();
			}
			m_outBuffer[m_outBufferPos++] = (byte)b;
		}

		internal void FlushBuffer()
		{
			m_outWriter.Write(m_outBuffer, 0, m_outBufferPos);
			m_outBufferPos = 0;
		}

		internal void Write(byte[] bytes, int start, int len)
		{
			if (m_outBufferPos + len > m_outBuffer.Length)
			{
				FlushBuffer();
				if (len >= m_outBuffer.Length)
				{
					m_outWriter.Write(bytes, start, len);
					return;
				}
			}
			Array.Copy(bytes, start, m_outBuffer, m_outBufferPos, len);
			m_outBufferPos += len;
		}

		internal void WriteOpAndData(int op, byte[] bytes, int start, int len)
		{
			if (m_outBufferPos + (len + 1) > m_outBuffer.Length)
			{
				FlushBuffer();
				if (len + 1 >= m_outBuffer.Length)
				{
					m_outWriter.Write(op);
					m_outWriter.Write(bytes, start, len);
					return;
				}
			}
			m_outBuffer[m_outBufferPos++] = (byte)op;
			Array.Copy(bytes, start, m_outBuffer, m_outBufferPos, len);
			m_outBufferPos += len;
		}

		internal void Reset(Stream outStream)
		{
			m_outWriter = new BinaryWriter(outStream);
			m_valueHeapSize = 0;
			m_numOps = 0;
			m_distinctKeysSize = 0;
			m_seenHashSize = 0;
			if (m_keysNeedReset)
			{
				for (int i = 0; i < m_keys.Length; i++)
				{
					int num = 0;
					while (m_keys[i] != null && num < m_keys[i].Length && m_keys[i][num] != 0)
					{
						m_keys[i][num] = 0;
						num++;
					}
				}
			}
			m_keysNeedReset = true;
			m_depth = 0;
			m_outBufferPos = 0;
			m_tinyNodeCount = 0;
			m_writeTinyNodeCount = true;
			m_ctx.Init();
			m_ctx.SetExceptionFactory(GetExceptionFactory());
		}

		internal void WriteTimestamp(byte[] raw)
		{
			if (raw.Length == OsonPrimitiveConversions.SIZE_TIMESTAMP)
			{
				FixedBinary(57, raw.Length, raw);
			}
			else
			{
				FixedBinary(125, raw.Length, raw);
			}
		}

		internal void WriteDate(byte[] raw)
		{
			FixedBinary(60, OsonPrimitiveConversions.SIZE_DATE, raw);
		}

		internal void WriteIntervalYM(byte[] raw)
		{
			FixedBinary(61, 5, raw);
		}

		internal void WriteIntervalDS(byte[] raw)
		{
			FixedBinary(62, 11, raw);
		}

		internal void FixedBinary(int op, int len, byte[] bytes)
		{
			if (len != bytes.Length)
			{
				throw new ArgumentException();
			}
			AddValue(bytes);
			AddOp(op);
			m_numChildren[m_numOps - 1] = bytes.Length;
			m_ctx.Primitive();
		}

		public void Close()
		{
			m_ctx.Close();
			try
			{
				InitializeKeyHeap();
				ComputeOffsets();
				WriteHeader();
				WriteNameDictionary();
				writeTreeNodeSegment();
				FlushBuffer();
				m_outWriter.Close();
			}
			catch (IOException ex)
			{
				throw OracleJsonExceptions.IO.Create(GetExceptionFactory(), ex);
			}
		}

		internal void WriteString(string value)
		{
			ExpandValueHeap(value.Length * 4);
			int num = WriteString(value, m_valueHeap, m_valueHeapSize);
			int len = num - m_valueHeapSize;
			m_valueHeapSize = num;
			WriteStringOp(num, len);
		}

		internal void WriteUTF8String(byte[] array, int offset, int len)
		{
			ExpandValueHeap(len);
			WriteStringOp(m_valueHeapSize = WriteUTF8String(array, offset, len, m_valueHeap, m_valueHeapSize), len);
		}

		internal void WriteStringOp(int newPos, int len)
		{
			if (len <= 31)
			{
				AddOp(len);
			}
			else if (len < 256)
			{
				AddOp(51);
			}
			else if (len < 65536)
			{
				AddOp(55);
			}
			else
			{
				AddOp(56);
			}
			m_numChildren[m_numOps - 1] = len;
			m_ctx.Primitive();
		}

		internal void WriteStartObject()
		{
			AddOp(128);
			Push(m_numOps - 1);
			m_ctx.StartObject();
		}

		internal void WriteStartArray()
		{
			AddOp(192);
			Push(m_numOps - 1);
			m_ctx.StartArray();
		}

		public void WriteEnd()
		{
			m_ctx.End();
			m_depth--;
			m_previousSiblingIdx = m_opStack[m_depth];
		}

		internal void WriteDouble(double value)
		{
			byte[] array = OsonPrimitiveConversions.DoubleToCanonicalFormatBytes(value);
			AddValue(array);
			AddOp(54);
			m_numChildren[m_numOps - 1] = array.Length;
			m_ctx.Primitive();
		}

		internal void WriteBoolean(bool value)
		{
			if (value)
			{
				AddOp(49);
			}
			else
			{
				AddOp(50);
			}
			m_ctx.Primitive();
		}

		internal void WriteOraNumber(OracleJsonDecimal value)
		{
			if (value.IsDec())
			{
				WriteDecimal(value.BigDecimalValue());
			}
			else if (value.IsSB4())
			{
				WriteSB4(value.IntValue());
			}
			else if (value.IsSB8())
			{
				WriteSB8(value.LongValue());
			}
			else
			{
				WriteNumber(value.Raw());
			}
		}

		internal void WriteDecimal(decimal value)
		{
			WriteNumber(OsonPrimitiveConversions.ToNumber(value), OsonConstants.MASK_DEC_16, 116);
		}

		internal void WriteDecimal(BigInteger value)
		{
			WriteNumber(OsonPrimitiveConversions.ToNumber(value), OsonConstants.MASK_DEC_16, 116);
		}

		internal void WriteSB4(int value)
		{
			byte[] array = OsonPrimitiveConversions.ToNumber(value);
			AddValue(array);
			AddOp(array.Length | OsonConstants.MASK_SB4);
			m_numChildren[m_numOps - 1] = array.Length;
			m_ctx.Primitive();
		}

		public void WriteSB8(long value)
		{
			byte[] array = OsonPrimitiveConversions.ToNumber(value);
			AddValue(array);
			AddOp(array.Length | OsonConstants.MASK_SB8);
			m_numChildren[m_numOps - 1] = array.Length;
			m_ctx.Primitive();
		}

		internal void WriteNumberAsString(decimal bd)
		{
			byte[] bytes = Encoding.UTF8.GetBytes(bd.ToString());
			if (bytes.Length > 256)
			{
				throw new ArgumentException();
			}
			AddValue(bytes);
			AddOp(53);
			m_numChildren[m_numOps - 1] = bytes.Length;
			m_ctx.Primitive();
		}

		internal void WriteBytes(byte[] bytes)
		{
			AddValue(bytes);
			AddOp((bytes.Length < 65536) ? 58 : 59);
			m_numChildren[m_numOps - 1] = bytes.Length;
			m_ctx.Primitive();
		}

		internal void WriteId(byte[] bytes)
		{
			AddValue(bytes);
			if (bytes.Length > 16)
			{
				throw new NotSupportedException();
			}
			AddOp(126);
			m_numChildren[m_numOps - 1] = bytes.Length;
			m_ctx.Primitive();
		}

		internal void WriteFloat(float value)
		{
			byte[] array = OsonPrimitiveConversions.FloatToCanonicalFormatBytes(value);
			AddValue(array);
			AddOp(127);
			m_numChildren[m_numOps - 1] = array.Length;
			m_ctx.Primitive();
		}

		internal void WriteKey(string key)
		{
			m_ctx.PendingKey();
			if (m_numOps >= m_ops.Length)
			{
				ExpandOp();
			}
			int num = OsonHeader.Ub1hash(key);
			int[] array = m_keys[num];
			if (array == null)
			{
				array = (m_keys[num] = new int[2]);
			}
			int i;
			for (i = 0; i < array.Length; i++)
			{
				int num2 = array[i] - 1;
				if (num2 == -1)
				{
					break;
				}
				if (m_distinctKeys[num2].Equals(key))
				{
					m_fieldIDs[m_numOps] = num2;
					return;
				}
			}
			if (i >= array.Length)
			{
				Array.Resize(ref m_keys[num], m_keys[num].Length * 2);
				array = m_keys[num];
			}
			else if (i == 0 && m_seenHashSize < SEEN_HASH_THRESHOLD)
			{
				m_seenHash[m_seenHashSize++] = num;
			}
			if (m_distinctKeysSize + 1 >= m_distinctKeys.Length)
			{
				Array.Resize(ref m_distinctKeys, m_distinctKeys.Length * 2);
				array = m_keys[num];
			}
			m_fieldIDs[m_numOps] = m_distinctKeysSize;
			m_distinctKeys[m_distinctKeysSize++] = key;
			array[i] = m_distinctKeysSize;
		}
	}
}
